<template>
	<div class="uploader-file" :status="status">
		<slot
			:file="file"
			:list="list"
			:status="status"
			:paused="paused"
			:error="error"
			:response="response"
			:average-speed="averageSpeed"
			:formated-average-speed="formatedAverageSpeed"
			:current-speed="currentSpeed"
			:is-complete="isComplete"
			:is-uploading="isUploading"
			:size="size"
			:formated-size="formatedSize"
			:uploaded-size="uploadedSize"
			:progress="progress"
			:progress-style="progressStyle"
			:progressing-class="progressingClass"
			:time-remaining="timeRemaining"
			:formated-time-remaining="formatedTimeRemaining"
			:type="type"
			:extension="extension"
			:file-category="fileCategory"
		>
			<div
				class="uploader-file-progress"
				:class="progressingClass"
				:style="progressStyle"
			></div>
			<div class="uploader-file-info">
				<div class="uploader-file-name">
					<i class="uploader-file-icon" :icon="fileCategory"></i>{{ file.name }}
				</div>
				<div class="uploader-file-size">{{ formatedSize }}</div>
				<div class="uploader-file-meta"></div>
				<div class="uploader-file-status">
					<span v-if="false" v-show="status !== 'uploading'">{{
						statusText
					}}</span>
					<span v-show="status === 'uploading'">
						<span>{{ progressStyle.progress }}</span>
						<em>{{ formatedAverageSpeed }}</em>
						<i>{{ formatedTimeRemaining }}</i>
					</span>
				</div>
				<div class="uploader-file-actions">
					<span class="uploader-file-pause" @click="pause"></span>
					<span v-if="false" class="uploader-file-resume" @click="resume"
						>️</span
					>
					<span v-if="false" class="uploader-file-retry" @click="retry"></span>
					<span
						v-if="false"
						class="uploader-file-remove"
						@click="remove"
					></span>
				</div>
			</div>
		</slot>
	</div>
</template>

<script>
import Uploader from 'simple-uploader.js'
import events from '../common/file-events'
import { secondsToStr } from '../common/utils'

const COMPONENT_NAME = 'uploader-file'

export default {
	name: COMPONENT_NAME,
	props: {
		file: {
			type: Object,
			default() {
				return {}
			}
		},
		list: {
			type: Boolean,
			default: false
		}
	},
	data() {
		return {
			response: null,
			paused: false,
			error: false,
			averageSpeed: 0,
			currentSpeed: 0,
			isComplete: false,
			isUploading: false,
			size: 0,
			formatedSize: '',
			uploadedSize: 0,
			progress: 0,
			timeRemaining: 0,
			type: '',
			extension: '',
			progressingClass: ''
		}
	},
	computed: {
		fileCategory() {
			const extension = this.extension
			const isFolder = this.file.isFolder
			let type = isFolder ? 'folder' : 'unknown'
			const categoryMap = this.file.uploader.opts.categoryMap
			const typeMap = categoryMap || {
				image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
				video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
				audio: ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'],
				document: [
					'doc',
					'txt',
					'docx',
					'pages',
					'epub',
					'pdf',
					'numbers',
					'csv',
					'xls',
					'xlsx',
					'keynote',
					'ppt',
					'pptx'
				]
			}
			Object.keys(typeMap).forEach((_type) => {
				const extensions = typeMap[_type]
				if (extensions.indexOf(extension) > -1) {
					type = _type
				}
			})
			return type
		},
		progressStyle() {
			const progress = Math.floor(this.progress * 100)
			const style = `translateX(${Math.floor(progress - 100)}%)`
			return {
				progress: `${progress}%`,
				webkitTransform: style,
				mozTransform: style,
				msTransform: style,
				transform: style
			}
		},
		formatedAverageSpeed() {
			return `${Uploader.utils.formatSize(this.averageSpeed)} / s`
		},
		status() {
			const isUploading = this.isUploading
			const isComplete = this.isComplete
			const isError = this.error
			const paused = this.paused
			if (isComplete) {
				return 'success'
			} else if (isError) {
				return 'error'
			} else if (isUploading) {
				return 'uploading'
			} else if (paused) {
				return 'paused'
			} else {
				return 'waiting'
			}
		},
		statusText() {
			const status = this.status
			const fileStatusText = this.file.uploader.fileStatusText
			let txt = status
			if (typeof fileStatusText === 'function') {
				txt = fileStatusText(status, this.response)
			} else {
				txt = fileStatusText[status]
			}
			return txt || status
		},
		formatedTimeRemaining() {
			const timeRemaining = this.timeRemaining
			const file = this.file
			if (timeRemaining === Number.POSITIVE_INFINITY || timeRemaining === 0) {
				return ''
			}
			let parsedTimeRemaining = secondsToStr(timeRemaining)
			const parseTimeRemaining = file.uploader.opts.parseTimeRemaining
			if (parseTimeRemaining) {
				parsedTimeRemaining = parseTimeRemaining(
					timeRemaining,
					parsedTimeRemaining
				)
			}
			return parsedTimeRemaining
		}
	},
	watch: {
		status(newStatus, oldStatus) {
			if (oldStatus && newStatus === 'uploading' && oldStatus !== 'uploading') {
				this.tid = setTimeout(() => {
					this.progressingClass = 'uploader-file-progressing'
				}, 200)
			} else {
				clearTimeout(this.tid)
				this.progressingClass = ''
			}
		}
	},
	methods: {
		_actionCheck() {
			this.paused = this.file.paused
			this.error = this.file.error
			this.isUploading = this.file.isUploading()
		},
		pause() {
			this.file.pause()
			this._actionCheck()
			this._fileProgress()
		},
		resume() {
			this.file.resume()
			this._actionCheck()
		},
		remove() {
			this.file.cancel()
		},
		retry() {
			this.file.retry()
			this._actionCheck()
		},
		processResponse(message) {
			let res = message
			try {
				res = JSON.parse(message)
			} catch (e) {
				console.log(e)
			}
			this.response = res
		},
		fileEventsHandler(event, args) {
			const rootFile = args[0]
			const file = args[1]
			const target = this.list ? rootFile : file
			if (this.file === target) {
				if (this.list && event === 'fileSuccess') {
					this.processResponse(args[2])
					return
				}
				this[`_${event}`].apply(this, args)
			}
		},
		_fileProgress() {
			this.progress = this.file.progress()
			this.averageSpeed = this.file.averageSpeed
			this.currentSpeed = this.file.currentSpeed
			this.timeRemaining = this.file.timeRemaining()
			this.uploadedSize = this.file.sizeUploaded()
			this._actionCheck()
		},
		_fileSuccess(rootFile, file, message) {
			if (rootFile) {
				this.processResponse(message)
			}
			this._fileProgress()
			this.error = false
			this.isComplete = true
			this.isUploading = false
		},
		_fileComplete() {
			this._fileSuccess()
		},
		_fileError(rootFile, file, message) {
			this._fileProgress()
			this.processResponse(message)
			this.error = true
			this.isComplete = false
			this.isUploading = false
		}
	},
	mounted() {
		const staticProps = ['paused', 'error', 'averageSpeed', 'currentSpeed']
		const fnProps = [
			'isComplete',
			'isUploading',
			{
				key: 'size',
				fn: 'getSize'
			},
			{
				key: 'formatedSize',
				fn: 'getFormatSize'
			},
			{
				key: 'uploadedSize',
				fn: 'sizeUploaded'
			},
			'progress',
			'timeRemaining',
			{
				key: 'type',
				fn: 'getType'
			},
			{
				key: 'extension',
				fn: 'getExtension'
			}
		]
		staticProps.forEach((prop) => {
			this[prop] = this.file[prop]
		})
		fnProps.forEach((fnProp) => {
			if (typeof fnProp === 'string') {
				this[fnProp] = this.file[fnProp]()
			} else {
				this[fnProp.key] = this.file[fnProp.fn]()
			}
		})

		const handlers = (this._handlers = {})
		const eventHandler = (event) => {
			handlers[event] = (...args) => {
				this.fileEventsHandler(event, args)
			}
			return handlers[event]
		}
		events.forEach((event) => {
			this.file.uploader.on(event, eventHandler(event))
		})
	},
	destroyed() {
		events.forEach((event) => {
			this.file.uploader.off(event, this._handlers[event])
		})
		this._handlers = null
	}
}
</script>

<style>
.uploader-file {
	position: relative;
	height: 49px;
	line-height: 49px;
	overflow: hidden;
	border-bottom: 1px solid #cdcdcd;
}
.uploader-file[status='waiting'] .uploader-file-pause,
.uploader-file[status='uploading'] .uploader-file-pause {
	display: block;
}
.uploader-file[status='paused'] .uploader-file-resume {
	display: block;
}
.uploader-file[status='error'] .uploader-file-retry {
	display: block;
}
.uploader-file[status='success'] .uploader-file-remove {
	display: none;
}
.uploader-file[status='error'] .uploader-file-progress {
	background: #ffe0e0;
}
.uploader-file-progress {
	position: absolute;
	width: 100%;
	height: 100%;
	background: #e2eeff;
	transform: translateX(-100%);
}
.uploader-file-progressing {
	transition: all 0.4s linear;
}
.uploader-file-info {
	position: relative;
	z-index: 1;
	height: 100%;
	overflow: hidden;
}
.uploader-file-info:hover {
	background-color: rgba(240, 240, 240, 0.2);
}
.uploader-file-info i,
.uploader-file-info em {
	font-style: normal;
}
.uploader-file-name,
.uploader-file-size,
.uploader-file-meta,
.uploader-file-status,
.uploader-file-actions {
	float: left;
	position: relative;
	height: 100%;
}
.uploader-file-name {
	width: 75%;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
	text-indent: 14px;
}
.uploader-file-icon {
	width: 24px;
	height: 24px;
	display: inline-block;
	vertical-align: top;
	margin-top: 13px;
	margin-right: 8px;
}
.uploader-file-icon::before {
	content: '📃';
	display: block;
	height: 100%;
	font-size: 24px;
	line-height: 1;
	text-indent: 0;
}
.uploader-file-icon[icon='folder']::before {
	content: '📂';
}
.uploader-file-icon[icon='image']::before {
	content: '📊';
}
.uploader-file-icon[icon='video']::before {
	content: '📹';
}
.uploader-file-icon[icon='audio']::before {
	content: '🎵';
}
.uploader-file-icon[icon='document']::before {
	content: '📋';
}
.uploader-file-size {
	width: 13%;
	text-indent: 10px;
}
.uploader-file-meta {
	width: 8%;
}
.uploader-file-status {
	width: 24%;
	text-indent: 20px;
}
.uploader-file-actions {
	width: 10%;
}
.uploader-file-actions > span {
	display: none;
	float: left;
	width: 16px;
	height: 16px;
	margin-top: 16px;
	margin-right: 10px;
	cursor: pointer;
	background: url('')
		no-repeat 0 0;
}
.uploader-file-actions > span:hover {
	background-position-x: -21px;
}
.uploader-file-actions .uploader-file-pause {
	background-position-y: 0;
}
.uploader-file-actions .uploader-file-resume {
	background-position-y: -17px;
}
.uploader-file-actions .uploader-file-retry {
	background-position-y: -53px;
}
.uploader-file-actions .uploader-file-remove {
	display: block;
	background-position-y: -34px;
}
</style>
